home *** CD-ROM | disk | FTP | other *** search
/ Disc to the Future 2 / Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin / MAC / THINKC / 3_0 / MIDIMERG / MIDIMERG.C < prev    next >
Text File  |  1988-12-08  |  9KB  |  356 lines

  1. /*    M i d i M e r g e r
  2.     Nick Rothwell, December '88.
  3.  */
  4.         
  5. #include "MIDI.h"                /*    The low-level driving routines. */
  6. #include "MidiMerger.h"            /*    MidiMerge's own typedefs/prototypes. */
  7.  
  8. typedef unsigned short COUNT;
  9. #define INFINITY    0xFFFF        /*    We measure MIDI messages with an
  10.                                     unsigned short, so don't exceed 64K
  11.                                     of system exclusive. */
  12.                                     
  13. #define SOX                0xF0        /*    Start of exclusive. */
  14. #define EOX                0xF7        /*    End of exclusive. */
  15. #define TOP_BIT            0x80
  16. #define REALTIME        0xF8        /*    Bits determining real-time. */
  17. #define ACTIVE_SENSE    0xFE        /*    Active-sensing real-time msg. */
  18.  
  19. /*    midiError *MUST* be provided by the client application - it can be null
  20.     if you want to live dangerously... message is a C ('\0' terminated)
  21.     string. The state of the MidiMerge library is undefined after it has
  22.     needed to call midiError. Perhaps I need a reset command... */
  23. extern midiError(char *message);
  24.  
  25. /*    Local/library prototypes. */
  26. static COUNT messageSize(BYTE status);
  27. static void putByte(PORT port, BYTE byte);
  28. static BYTE mustReadByte(PORT port);
  29. static swallowRealtime(PORT input, PORT output, BYTE *byte);
  30. static BYTE channeliseByte(BYTE byte, int channel);
  31. static echoMessage(PORT input, PORT output, BYTE firstByte);
  32. static assert(char *message, Boolean cond);
  33. extern printf(char *template, ...);
  34. static diagnostic(char *template, ...);
  35.  
  36. static BYTE inputStatus[2] = {0, 0},
  37.             outputStatus[2] = {0, 0};
  38.                 /*    Keep tracks of the last status bytes for both ports. To
  39.                     do merging, we keep track of the assumed status bytes
  40.                     on input and output. */
  41.  
  42. Boolean channelling[2] = {FALSE, FALSE};
  43.                             /*    Are we channelising the input? */
  44. int mockChannel[2];            /*    If so, the mock channel. */
  45.  
  46. assert(message, cond)
  47. char *message;
  48. Boolean cond;
  49. {
  50.     if (!cond)  midiError(message);
  51. }
  52.  
  53. diagnostic(template, a, b, c, d, e, f)
  54. char *template;
  55. {
  56.     printf(template, a, b, c, d, e, f);
  57. }
  58.  
  59. /*    diagnostics: uncomment the first #define if you want them. */
  60.  
  61. /*    #define DIAGNOSTIC(args)    diagnostic args; */
  62. #define DIAGNOSTIC(args)    ;    /*Just the semicolon. */
  63.  
  64. /*    messageSize: how many data bytes to a message with this status? */
  65. COUNT messageSize(status)
  66. BYTE status;
  67. {
  68.     switch (status&0xF0)        /*    Discard lowest nybble (it's the channel,
  69.                                     except for the system messages). */
  70.     {
  71.         case 0x80:                /*    NOTE OFF. */
  72.         case 0x90:                /*    NOTE ON. */
  73.         case 0xA0:                /*    POLYPHONIC AFTERTOUCH. */
  74.         case 0xB0:                /*    CONTROL CHANGE. */
  75.             return(2);
  76.  
  77.         case 0xC0:                /*    PROGRAM CHANGE. */
  78.         case 0xD0:                /*    CHANNEL AFTERTOUCH. */
  79.             return(1);
  80.             
  81.         case 0xE0:                /*    PITCH-WHEEL. */
  82.             return(2);
  83.             
  84.         case 0xF0:                /*    SYSTEM MESSAGE... */
  85.             switch (status)
  86.             {
  87.                /* Common: */
  88.                 case 0xF0:        /*    START OF EXCLUSIVE. */
  89.                     return(INFINITY);
  90.                     
  91.                 case 0xF1:        /*    MIDI TIME CODE 1/4 FRAME. */
  92.                     return(2);
  93.                     
  94.                 case 0xF2:        /*    SONG POSITION POINTER. */
  95.                     return(2);
  96.                     
  97.                 case 0xF3:        /*    SONG SELECT. */
  98.                     return(1);
  99.                     
  100.                 case 0xF4:        /*    undefined. */
  101.                 case 0xF5:        /*    undefined. */
  102.                     midiError("Unexpected status byte: 0xF4/0xF5");
  103.                     
  104.                 case 0xF6:        /*    TUNE REQUEST. */
  105.                 case 0xF7:        /*    END OF EXCLUSIVE. */
  106.                     return(0);
  107.                     
  108.                /* Realtime: */
  109.                 case 0xF8:        /*    TIMING CLOCK. */
  110.                 case 0xF9:        /*    undefined. */
  111.                 case 0xFA:        /*    START. */
  112.                 case 0xFB:        /*    CONTINUE. */
  113.                 case 0xFC:        /*    STOP. */
  114.                 case 0xFD:        /*    undefined. */
  115.                 case 0xFE:        /*    ACTIVE SENSING. */
  116.                 case 0xFF:        /*    SYSTEM RESET. */
  117.                     midiError("Unexpected real-time byte.");
  118.             }
  119.     }
  120.     
  121.     midiError("messageSize: couldn't switch");
  122. }
  123.  
  124. void channelise(port, channel)
  125. PORT port;
  126. int channel;
  127. {
  128.     channelling[port] = TRUE;
  129.     mockChannel[port] = channel;
  130. }
  131.  
  132. void noChannelise(port)
  133. PORT port;
  134. {
  135.     channelling[port] = FALSE;
  136. }
  137.  
  138. void putByte(port, byte)
  139. PORT port;
  140. BYTE byte;
  141. {
  142.     switch (port)
  143.     {
  144.         case MODEM:        txMidiA(byte);    break;
  145.         case PRINTER:    txMidiB(byte);
  146.     }
  147. }
  148.  
  149. BYTE mustReadByte(port)
  150. PORT port;
  151. {
  152.     long input;
  153.     
  154.     do
  155.     {
  156.         input = ((port == MODEM) ? rxMidiA() : rxMidiB());
  157.     }
  158.     while (input == 0L);
  159.     
  160.     return(input&0xFF);
  161. }
  162.  
  163. /*    swallowRealtime: passed a byte by reference. If the byte is a
  164.     System real-time byte, echo it out and busy-wait for a real one, and
  165.     assign the byte with this. swallowRealtime is fine when half way
  166.     through processing a message, but DON'T call it when waiting for a
  167.     message, as then an arriving real-time byte will have you busy-wait
  168.     until the next interesting input. */
  169.     
  170. static swallowRealtime(input, output, byte)
  171. PORT input, output;
  172. BYTE *byte;
  173. {
  174.     while (((*byte)&REALTIME) == REALTIME)
  175.     {
  176.         if (*byte != ACTIVE_SENSE)  putByte(output, *byte);
  177.                 /*    Don't echo active sensing bytes. */
  178.         *byte = mustReadByte(input);
  179.     }
  180. }
  181.  
  182. BYTE channeliseByte(byte, channel)
  183. BYTE byte;
  184. int channel;
  185. {
  186.     int topBits = byte&0xF0;
  187.  
  188.     if (topBits == 0xF0)            /* Don't do the system messages. */
  189.         return(byte);
  190.     else
  191.         return(topBits|(channel-1));
  192. }
  193.  
  194. /*    echoMessage: we've just seen "firstByte" on the input port, so we
  195.     swallow an entire MIDI message. "firstByte" may be a status byte, or
  196.     we may have to assume running status. */
  197.  
  198. echoMessage(input, output, firstByte)
  199. PORT input, output;
  200. BYTE firstByte;
  201. {
  202.     int dataBytesIn;
  203.     COUNT len, i;
  204.     BYTE nextByte;
  205.     
  206.     DIAGNOSTIC(("echoMessageA: %x\n", firstByte))
  207.     
  208.     if (firstByte&TOP_BIT)                /*    Status byte? */
  209.     {
  210.       /*I think it's safe to channelise the status byte first, and then
  211.           use the new one for input running status. If the external device
  212.           changes channel, it will re-send status byte, but I'll just see
  213.           it as running status. */
  214.           
  215.           if (channelling[input])
  216.               firstByte = channeliseByte(firstByte, mockChannel[input]);
  217.               
  218.         inputStatus[input] = firstByte;
  219.         dataBytesIn = 0;            /*    Haven't read any data bytes yet.
  220.                                         (there may not be any!) */
  221.     }
  222.     else
  223.         dataBytesIn = 1;            /*    We have the first data byte. */
  224.         
  225.     if (inputStatus[input] != outputStatus[output])
  226.                         /*    Input status byte differs from the
  227.                             running status byte on the output. */
  228.     {
  229.         putByte(output, inputStatus[input]);
  230.         outputStatus[output] = inputStatus[input];
  231.         DIAGNOSTIC(("Set output status %x\n", outputStatus[output]))
  232.     }
  233.     
  234.     if (dataBytesIn == 1)  putByte(output, firstByte);
  235.                                     /*    Echo out the first data byte, if
  236.                                         we've read it. */
  237.                                         
  238.     len = messageSize(inputStatus[input]);
  239.     DIAGNOSTIC(("Message size(%x)=%d\n", inputStatus[input], len))
  240.     for (i = dataBytesIn; i < len; i++)
  241.     {        /*    Read and echo the outstanding message, but be prepared for
  242.                 a status byte before the loop exits - we do System Exclusives
  243.                 by assuming infinite length, and dropping out when we hit
  244.                 the EOX. */
  245.         nextByte = mustReadByte(input);
  246.         swallowRealtime(input, output, &nextByte);
  247.         if (nextByte & TOP_BIT)        /*    Yup, here's a status byte. It should
  248.                                         only be an EOX, and we should only be
  249.                                         processing an SOX. */
  250.         {
  251.             assert("Unexpected status byte",
  252.                    (nextByte == EOX) && (inputStatus[input] == SOX)
  253.                   );
  254.             putByte(output, EOX);
  255.             outputStatus[output] = EOX;
  256.             return;
  257.         }
  258.         else
  259.             putByte(output, nextByte);
  260.     }
  261.     
  262. }
  263.  
  264. /*    Idle: this *MUSTN'T* be called if the Mac application is half way
  265.     through outputting a message. It can't, I hope, since I only provide
  266.     routines for outputting entire messages. */
  267. void idleMidi(input, output)
  268. PORT input, output;
  269. {
  270.     long inWord;
  271.     BYTE inRealtime;
  272.     Boolean going = TRUE;
  273.     
  274.     while (going)
  275.     {
  276.        /*Assumption: we NEVER leave external MIDI messages half-done. */
  277.         inWord = ((input == MODEM) ? rxMidiA() : rxMidiB());
  278.         if (inWord != 0L)        /*    Something from external source. */
  279.         {
  280.             if ((inRealtime = (inWord&REALTIME)) == REALTIME)
  281.             {                    /*    Real-time: just echo and return. */
  282.                 DIAGNOSTIC(("Real-time idle.\n"))
  283.                 
  284.                 if (inRealtime != ACTIVE_SENSE)
  285.                     putByte(output, inRealtime);
  286.                     
  287.                 going = FALSE;
  288.             }
  289.             else
  290.                 echoMessage(input, output, inWord&0xFF);
  291.         }                        /*    Swallow and echo the entire message,
  292.                                     loop round to see if there's more. */
  293.         else
  294.             going = FALSE;        /*    If there's nothing, return. */
  295.     }
  296. }
  297.  
  298. /*    transmitMidi: takes a message (sequence of bytes), the first of which *MUST*
  299.     be a status byte. It interprets this to calculate the message length, and
  300.     outputs the message atomically.
  301.         System exclusives must have the terminating EOX, which is also sent
  302.     out. */
  303.     
  304. void transmitMidi(port, message)
  305. PORT port;
  306. BYTE *message;
  307. {
  308.     BYTE status = *message, b;
  309.     COUNT len, i;
  310.     assert("sendMidiA: expecting status byte", (status & TOP_BIT) != 0);
  311.     
  312.     if (outputStatus[port] != status)
  313.     {
  314.         putByte(port, status);
  315.         outputStatus[port] = status;
  316.     }
  317.     
  318.     len = messageSize(status);
  319.     for (i = 0; i < len; i++)
  320.     {
  321.         b = message[i+1];
  322.         if (b & TOP_BIT)    /*    Status byte? */
  323.         {
  324.             assert("transmitMidi: unexpected status byte",
  325.                    (status == SOX) && (b == EOX)
  326.                   );
  327.             putByte(port, EOX);
  328.             outputStatus[port] = EOX;
  329.             return;
  330.         }
  331.         else
  332.             putByte(port, b);
  333.     }
  334. }
  335.  
  336. void startMidi(port)
  337. PORT port;
  338. {
  339.     switch (port)
  340.     {
  341.         case MODEM:        initSccA();    break;
  342.         case PRINTER:    initSccB();
  343.     }
  344. }
  345.  
  346. void stopMidi(port)
  347. PORT port;
  348. {
  349.     switch (port)
  350.     {
  351.         case MODEM:        resetSccA();    break;
  352.         case PRINTER:    resetSccB();
  353.     }
  354. }
  355.  
  356.